/**
 * Copyright (c) 2012, Brent Mucci, Ken Anderson
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/

#include <iostream>
#include <string>
#include <deque>
#include "mcl_nal.hpp"
#include "mcl_timer.h"
#include "mcl_nal_reader.hpp"

// Set these definitions to test what you like.
#define TEST_BITS             false
#define TEST_NAL_PARSING      false
#define TEST_NAL_TIMING       true
#define TEST_NAL_COUNTING     false
#define TEST_NAL_PARAMETERS	  true

void print_bits(unsigned int bits, unsigned int numBits)
{
	unsigned int mask = 0x01 << 31;
	for(unsigned int i = 0; i < numBits; i++)
	{
		if(bits & mask)
		{
			std::cout << 1;
		}
		else
		{
			std::cout << 0;
		}
		bits = bits << 1;
	}
}

// Print first 3200 bits from a file.
void test_bits(std::ifstream &a_ifstream)
{
	mcl::BitstreamBuffer bsb(a_ifstream);
	unsigned int bits = 1;
	unsigned int numBits = 32;
	for(int i = 0; i < 100 && bsb.more_data_in_byte_stream(); i++)
	{
		bits = bsb.read_bits(numBits);
		print_bits(bits, numBits);
		std::cout << std::endl;
	}
}

// Test the NALs from a h264 stream
void test_nal_parsing(std::ifstream &stream, std::ifstream &fsNalCounter)
{
	mcl::BitstreamBuffer bitstream(stream);
	mcl::NALReader nalReader(fsNalCounter);
	unsigned int i = 1, nalSize = 0;
	mcl::NAL nal;
	while(nalSize = nalReader.getNextNALSize())
	{
		nal.parse(bitstream, nalReader.getNextNALSize());
		std::printf("NAL #%3d : type=%2d, size=%4d\n", i, nal.m_nal_unit_type, nal.m_numBytesInNALunit);
		i++;
	}
}

// Test the NALs parameters and decoding from a h264 stream
void test_nal_parameters(std::ifstream &stream, std::ifstream &fsNalCounter)
{
	mcl::BitstreamBuffer bitstream(stream);
	mcl::NALReader nalReader(fsNalCounter);
	unsigned int i = 1, nalSize = 0;
	mcl::NAL nal;
	while(nalSize = nalReader.getNextNALSize())
	{
		nal.parse(bitstream, nalReader.getNextNALSize());
		std::printf("NAL #%3d : type=%2d, size=%4d\n", i, nal.m_nal_unit_type, nal.m_numBytesInNALunit);
		i++;
	}
}

// Test the NALs from a h264 stream
void test_nal_counting(std::ifstream &stream)
{
	mcl::NALReader nalReader(stream);
	unsigned int i = 1;
	mcl::NAL nal;
	unsigned int nalSize;
	while((nalSize = nalReader.getNextNALSize()))// && i < 25)
	{
	//for(int i = 0; i < 10; i++){
	//nalSize = nalReader.getNextNALSize();
		std::printf("NAL #%3d : size=%4d\n", i, nalSize);
		i++;
	}
	//}
}

void test_nal_timing(std::ifstream &stream, std::ifstream &fsNalCounter)
{
	mcl::Timer timer;
	mcl::BitstreamBuffer bitstream(stream);
	mcl::NALReader nalReader(fsNalCounter);
	unsigned int nalSize = 0;
	mcl::NAL nal;
	timer.start();
	while(nalSize = nalReader.getNextNALSize())
	{
		nal.parse(bitstream, nalReader.getNextNALSize());
	}

	std::printf("time to parse = %f\n", timer.getSeconds());
}

int main(int argc, char **argv)
{
	std::ifstream fileStream("../../../../../input/h264/manitu.264", std::ios::in|std::ios::binary);
	std::ifstream fsNALCounter("../../../../../input/h264/manitu.264", std::ios::in|std::ios::binary);
	
	if( TEST_BITS )
	{
		printf("Testing bitstream\n");
		fileStream.seekg (std::ios::beg);
		test_bits(fileStream);
		printf("\n");
	}

	if( TEST_NAL_COUNTING )
	{
		printf("Testing NAL counter\n");
		fsNALCounter.seekg (std::ios::beg);
		test_nal_counting(fsNALCounter);
		printf("\n");
	}

	if( TEST_NAL_PARSING )
	{
		printf("Testing NAL parsing\n");
		fileStream.seekg (std::ios::beg);
		fsNALCounter.seekg (std::ios::beg);
		test_nal_parsing(fileStream, fsNALCounter);
		printf("\n");
	}
	
	if( TEST_NAL_TIMING )
	{
		printf("Testing NAL parsing timing\n");
		fileStream.seekg (std::ios::beg);
		fsNALCounter.seekg (std::ios::beg);
		test_nal_timing(fileStream, fsNALCounter);
		printf("\n");
	}

	if ( TEST_NAL_PARAMETERS )
	{
		printf("Testing NAL parameters\n");
		fileStream.seekg (std::ios::beg);
		fsNALCounter.seekg (std::ios::beg);
		test_nal_parameters(fileStream, fsNALCounter);
		printf("\n");
	}

	fileStream.close();
	fsNALCounter.close();
	std::cout << std::endl << "Hit enter to escape";
	std::cin.get(); 
	return 0;
}